package com.franklin.ideaplugin.api.ui.editor;

import com.franklin.ideaplugin.api.actions.MyFindAllAction;
import com.franklin.ideaplugin.mybatisrunner.constant.Constants;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.execution.impl.ConsoleViewUtil;
import com.intellij.find.*;
import com.intellij.ide.highlighter.HighlighterFactory;
import com.intellij.ide.highlighter.HtmlFileType;
import com.intellij.ide.highlighter.XmlFileType;
import com.intellij.json.JsonFileType;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.EditorSettings;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.pom.Navigatable;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.PsiType;
import com.intellij.ui.EditorTextField;
import com.intellij.util.LocalTimeCounter;
import com.intellij.util.ui.JBUI;
import lombok.Getter;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.incremental.GlobalContextKey;

import javax.swing.border.Border;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;

/**
 * @author ZhangYuanSheng
 */
@Getter
@Setter
public class CustomEditor extends EditorTextField {

    /**
     * 文本格式
     */
    public static final FileType TEXT_FILE_TYPE = FileTypes.PLAIN_TEXT;
    /**
     * json格式
     */
    public static final FileType JSON_FILE_TYPE = JsonFileType.INSTANCE;
    /**
     * html格式
     */
    public static final FileType HTML_FILE_TYPE = HtmlFileType.INSTANCE;
    /**
     * xml格式
     */
    public static final FileType XML_FILE_TYPE = XmlFileType.INSTANCE;

    /**
     * json类型使用类
     */
    private PsiType psiType;

    private volatile Navigatable navigateElement = null;

    public static GlobalContextKey<CustomEditor> EASY_TESTING_EDITOR = GlobalContextKey.create("MYBATIS_RUNNER_EDITOR");

    /**
     * 子编辑器
     */
    private CustomEditor childEditor;

    /**
     * 父编辑器
     */
    private CustomEditor parentEditor;

    /**
     * 关闭回调
     */
    private Runnable closeCallback;

    /**
     * 搜索工具栏
     */
    private static Field searchReplaceComponent_mySearchActionsToolbar;

    static {
        try {
            searchReplaceComponent_mySearchActionsToolbar = SearchReplaceComponent.class.getDeclaredField("mySearchActionsToolbar");
            searchReplaceComponent_mySearchActionsToolbar.setAccessible(true);
        } catch (NoSuchFieldException e) {
            //ignore
            searchReplaceComponent_mySearchActionsToolbar = null;
        }
    }

    /**
     * 初始化器
     */
    protected List<Consumer<EditorEx>> initializerList = Lists.newArrayList();

    public CustomEditor(Project project) {
        this(project, TEXT_FILE_TYPE);
    }

    public CustomEditor(Project project, FileType fileType) {
        super(null, project, fileType, false, false);
    }

    public static void setupTextFieldEditor(@NotNull EditorEx editor, FileType fileType) {
        EditorSettings settings = editor.getSettings();
        settings.setFoldingOutlineShown(true);
        settings.setLineNumbersShown(true);
        settings.setIndentGuidesShown(true);
        editor.setHorizontalScrollbarVisible(true);
        editor.setVerticalScrollbarVisible(true);
        settings.setLineMarkerAreaShown(true);
        settings.setUseSoftWraps(true);
        settings.setFoldingOutlineShown(true);
        editor.setHighlighter(HighlighterFactory.createHighlighter(editor.getProject(), fileType));
        editor.setEmbeddedIntoDialogWrapper(true);
    }

    public void setText(@Nullable final String text, @NotNull final FileType fileType) {
//        if (getFileType().equals(fileType)) {
//            super.setText(text);
//        }else {
            super.setFileType(fileType);
            Document document = createDocument(text, fileType);
            setDocument(document);
//        }
    }

    @Override
    public void setFileType(@NotNull FileType fileType) {
        setNewDocumentAndFileType(fileType, createDocument(getText(), fileType));
    }

    private void initOneLineModePre(@NotNull final EditorEx editor) {
        editor.setOneLineMode(false);
        editor.setColorsScheme(editor.createBoundColorSchemeDelegate(null));
        editor.getSettings().setCaretRowShown(false);
    }

    /**
     * 添加 初始化要执行的动作
     *
     * @param initializer
     */
    public void addInitializer(Consumer<EditorEx> initializer) {
        this.initializerList.add(initializer);
    }

    @NotNull
    @Override
    protected EditorEx createEditor() {
        EditorEx editor = super.createEditor();
        initOneLineModePre(editor);
        this.initializerList.forEach(initializer -> initializer.accept(editor));
        setupTextFieldEditor(editor, getFileType());
        this.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                if (!e.isControlDown()) {
                    return;
                }
                if (e.getButton() == MouseEvent.BUTTON1) {
                    if (Objects.nonNull(navigateElement)) {
                        ApplicationManager.getApplication().invokeLater(() -> {
                            if (Objects.nonNull(navigateElement)) {
                                navigateElement.navigate(true);
                                navigateElement = null;
                            }
                        });
                    }
                }
            }

        });

        //ctrl + f
        editor.getContentComponent().addKeyListener(new KeyAdapter() {

            /**
             * 搜索按钮
             */
            private Set<Integer> searchKeySet = Sets.newHashSet(
                    KeyEvent.VK_F,
                    KeyEvent.VK_R
            );

            @Override
            public void keyPressed(KeyEvent e) {
                if (searchKeySet.contains(e.getKeyCode())) {
                    if (e.isControlDown()) {
                        FindModel findModel = new FindModel();
                        FindModel.initStringToFind(findModel, editor.getSelectionModel().getSelectedText());
                        EditorSearchSession editorSearchSession = EditorSearchSession.start(editor, findModel, getProject());

                        SearchReplaceComponent searchReplaceComponent = editorSearchSession.getComponent();
                        try {
                            ActionToolbarImpl actionToolbar = (ActionToolbarImpl) searchReplaceComponent_mySearchActionsToolbar.get(searchReplaceComponent);
                            List<AnAction> actions = actionToolbar.getActions();
                            DefaultActionGroup actionGroup = (DefaultActionGroup) actionToolbar.getActionGroup();
                            AnAction findAction = actions.stream()
                                    .filter(anAction -> anAction instanceof FindAllAction)
                                    .findFirst()
                                    .orElse(null);
                            if (Objects.nonNull(findAction)) {
                                actionGroup.replaceAction(findAction, new MyFindAllAction(getProject()));
                            }
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }

                        boolean consoleViewEditor = ConsoleViewUtil.isConsoleViewEditor(editor);
                        FindUtil.configureFindModel(
                                e.getKeyCode() == KeyEvent.VK_R,
                                editor,
                                findModel,
                                consoleViewEditor
                        );
                        if (!consoleViewEditor && editor.getSelectionModel().hasSelection()) {
                            // selection is used as string to find without search model modification so save the pattern explicitly
                            FindUtil.updateFindInFileModel(getProject(), findModel, true);
                        }
                    }
                }
            }
        });

        return editor;
    }

    @Override
    public void repaint(long tm, int x, int y, int width, int height) {
        super.repaint(tm, x, y, width, height);
        if (getEditor() instanceof EditorEx) {
            initOneLineModePre(((EditorEx) getEditor()));
        }
    }

    @Override
    public void setBorder(Border border) {
        super.setBorder(JBUI.Borders.empty());
    }

    public Document createDocument(@Nullable final String text, @NotNull final FileType fileType) {
        final PsiFileFactory factory = PsiFileFactory.getInstance(getProject());
        final long stamp = LocalTimeCounter.currentTime();
        final PsiFile psiFile = factory.createFileFromText(
                Constants.NAME,
                fileType,
                text == null ? "" : text,
                stamp,
                true,
                false
        );
        psiFile.putUserData(EASY_TESTING_EDITOR, this);
        return PsiDocumentManager.getInstance(getProject()).getDocument(psiFile);
    }

    /**
     * 关闭
     */
    public void close() {
        parentEditor = null;
        //回调
        if (Objects.nonNull(closeCallback)) {
            closeCallback.run();
        }
        //子编辑器
        if (Objects.nonNull(childEditor)) {
            childEditor.close();
            childEditor = null;
        }
    }

}
